/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ar.edu.unicen.twitter.suggest.backend.services;

import ar.edu.unicen.twitter.suggest.backend.datasources.TwitterFacade;
import ar.edu.unicen.twitter.suggest.backend.datasources.dao.ReportDao;
import ar.edu.unicen.twitter.suggest.backend.datasources.dao.TwitterDatasetDao;
import ar.edu.unicen.twitter.suggest.backend.dtos.RecommendationReport;
import ar.edu.unicen.twitter.suggest.backend.recommendation.CompararUsuarios.CompararPorOcurrencias;
import ar.edu.unicen.twitter.suggest.backend.recommendation.CompararUsuarios.CompararPorPosicion;
import ar.edu.unicen.twitter.suggest.backend.recommendation.CompararUsuarios.CompararPorScore;
import ar.edu.unicen.twitter.suggest.backend.recommendation.CompararUsuarios.CompararPorScoreExterno;
import ar.edu.unicen.twitter.suggest.backend.recommendation.CompararUsuarios.CompararUsuarios;
import ar.edu.unicen.twitter.suggest.backend.recommendation.CriterioUsuario.CriterioContar;
import ar.edu.unicen.twitter.suggest.backend.recommendation.CriterioUsuario.CriterioFuente;
import ar.edu.unicen.twitter.suggest.backend.recommendation.CriterioUsuario.CriterioUsuario;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.RankingUsuarios;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.RankingUsuariosCompuesto;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.RankingUsuariosCompuestoLineal;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.RankingUsuariosHannon;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.RankingUsuariosHannonS6;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.RankingUsuariosIS;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.RankingUsuariosMenciones;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.RankingUsuariosPeso;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.combinar.CombinadorPuntajeFactor;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.combinar.CombinadorPuntajePrioritario;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.combinar.CombinadorPuntajeProducto;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.combinar.CombinadorResultados;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.combinar.CombinadorResultadosInterseccion;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.combinar.CombinadorResultadosUnion;
import ar.edu.unicen.twitter.suggest.backend.recommendation.Ranking.combinar.CombinadorResultadosUnionPrioritario;
import ar.edu.unicen.twitter.suggest.backend.recommendation.crawling.DescartadorUsuarios;
import ar.edu.unicen.twitter.suggest.backend.recommendation.crawling.Recolector;
import ar.edu.unicen.twitter.suggest.backend.recommendation.crawling.NivelRecoleccion;
import ar.edu.unicen.twitter.suggest.backend.recommendation.crawling.RecoleccionFactory;
import ar.edu.unicen.twitter.suggest.backend.recommendation.crawling.RecoleccionFactory.TipoNivel;
import ar.edu.unicen.twitter.suggest.backend.recommendation.crawling.RecoleccionFactory.TipoTransicion;
import ar.edu.unicen.twitter.suggest.backend.recommendation.crawling.RecolectorApi.RecolecionApiFactory;
import ar.edu.unicen.twitter.suggest.backend.recommendation.crawling.RecolectorDataset.RecolecionDatasetFactory;
import ar.edu.unicen.twitter.suggest.commons.dtos.Usuario;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

/**
 *
 * @author Alex
 */
@Controller("recommendationController")
public class RecommendationController implements RecommendationControllerInterface {

    private final static Logger LOGGER = Logger.getLogger(RecommendationController.class.getName());
    @Autowired
    TwitterFacade twitterFacade;
    @Autowired
    ReportDao reportDao;
    RecoleccionFactory recoleccionFactory;
    private CriterioUsuario criterioTodos = null;
    private CriterioUsuario criterioFuente = new CriterioFuente();
    @Autowired
    TwitterDatasetDao twitterDatasetDao;
    int MAXPOSICIONBUSQUEDA = 300; //-1 por sin limite

    public RecommendationController() {
    }

    @PostConstruct
    public void init() {
        //recoleccionFactory = new RecolecionDatasetFactory(twitterDatasetDao);
        recoleccionFactory = new RecolecionApiFactory(twitterFacade);
    }

    private DescartadorUsuarios getDescartador(int offsetDescartador, float holdoutRatio) {
        //return new DescartadorUsuarios(new CriterioUsuarioAnd(new CriterioContar(holdoutRatio, offsetDescartador), new CriterioFuente()), /*descarteminimo=*/ 0);
        return new DescartadorUsuarios(new CriterioContar(holdoutRatio, offsetDescartador), /*descarteminimo=*/ 0);
    }

    private Recolector recolectorAmigos(RecoleccionFactory nivelFactory, TipoTransicion tipoTransicion, DescartadorUsuarios descartador, int nivelIntermedio, int nivelFinal, CompararUsuarios comparar) {
        ArrayList<NivelRecoleccion> niveles = new ArrayList<NivelRecoleccion>();
        NivelRecoleccion nivel1 = nivelFactory.getNivelRecoleccion(TipoNivel.NivelSeguidos, tipoTransicion, criterioTodos, comparar, nivelIntermedio);
        nivel1.setDescartador(descartador);
        niveles.add(nivel1);
        NivelRecoleccion nivel2 = nivelFactory.getNivelRecoleccion(TipoNivel.NivelSeguidos, tipoTransicion, criterioTodos, comparar, nivelFinal);
        niveles.add(nivel2);
        Recolector recolector = new Recolector(niveles);
        return recolector;
    }

    private Recolector recolectorFuentes(RecoleccionFactory nivelFactory, TipoTransicion tipoTransicion, DescartadorUsuarios descartador, int nivelIntermedio, int nivelFinal, CompararUsuarios comparar) {
        int iteraciones = 1;
        ArrayList<NivelRecoleccion> niveles = new ArrayList<NivelRecoleccion>();
        for (int iteracion = 0; iteracion < iteraciones; ++iteracion) {
            NivelRecoleccion nivel1 = nivelFactory.getNivelRecoleccion(TipoNivel.NivelSeguidos, tipoTransicion, criterioTodos, comparar, nivelIntermedio);
            niveles.add(nivel1);
            NivelRecoleccion nivel2 = nivelFactory.getNivelRecoleccion(TipoNivel.NivelSeguidores, tipoTransicion, criterioTodos, comparar, nivelIntermedio);
            niveles.add(nivel2);
        }
        NivelRecoleccion nivelUltimo = nivelFactory.getNivelRecoleccion(TipoNivel.NivelSeguidos, tipoTransicion, criterioTodos, comparar, nivelFinal);
        niveles.add(nivelUltimo);
        niveles.get(0).setDescartador(descartador);
        Recolector recolector = new Recolector(niveles);
        return recolector;
    }

    private boolean isFuente(Usuario u) {
        return criterioFuente.cumple(u);
    }

    private boolean isAmigo(Usuario u) {
        return !criterioFuente.cumple(u);
    }

    private List<Usuario> getFuentes(Collection<Usuario> usuarios) {
        List<Usuario> result = new ArrayList<Usuario>();
        for (Usuario u : usuarios) {
            if (isFuente(u)) {
                result.add(u);
            }
        }
        return result;
    }

    private List<Usuario> getFuentes(Collection<Usuario> usuarios, int evaluar) {
        List<Usuario> result = new ArrayList<Usuario>();
        int u = 0;
        for (Iterator<Usuario> usIt = usuarios.iterator(); usIt.hasNext() && u < evaluar;) {
            Usuario usuario = usIt.next();
            if (isFuente(usuario)) {
                result.add(usuario);
            }
            ++u;
        }
        return result;
    }

    private List<Usuario> getAmigos(Collection<Usuario> usuarios) {
        List<Usuario> result = new ArrayList<Usuario>();
        for (Usuario u : usuarios) {
            if (isAmigo(u)) {
                result.add(u);
            }
        }
        return result;
    }

    private List<Usuario> getAmigos(Collection<Usuario> usuarios, int evaluar) {
        List<Usuario> result = new ArrayList<Usuario>();
        int u = 0;
        for (Iterator<Usuario> usIt = usuarios.iterator(); usIt.hasNext() && u < evaluar;) {
            Usuario usuario = usIt.next();
            if (isAmigo(usuario)) {
                result.add(usuario);
            }
            ++u;
        }
        return result;
    }

    private List<Usuario> getEncontrados(Collection<Usuario> usuarios) {
        List<Usuario> result = new ArrayList<Usuario>();
        for (Usuario u : usuarios) {
            if (u.getPosicion() != RecommendationReport.POSICION_NO_ENCONTRADO) {
                result.add(u);
            }
        }
        return result;
    }

    private int contarFuentes(Collection<Usuario> usuarios) {
        int result = 0;
        for (Usuario u : usuarios) {
            if (isFuente(u)) {
                result++;
            }
        }
        return result;
    }

    @Override
    public List<RecommendationReport> recomendar(long id, int nIntermedio, int nFinal, int nMaxArcos, int iteraciones, float holdoutRatio, boolean permitirRedundancia) {

        recoleccionFactory.setMaxArcos(nMaxArcos);
        List<RecommendationReport> reports = null;
        Usuario usuarioInicial = recoleccionFactory.getUsuario(id);
        if (usuarioInicial != null) {
            reports = new ArrayList<RecommendationReport>();
            Collection<Usuario> seguidos = recoleccionFactory.getSeguidos(usuarioInicial);
            if (seguidos != null && !seguidos.isEmpty()) {
                int cantidadFuentes = contarFuentes(seguidos);
                for (int iteracion = 0; iteracion < iteraciones; ++iteracion) {
                    Date start = new Date();
                    ArrayList<Usuario> restantes = new ArrayList<Usuario>(seguidos);

                    DescartadorUsuarios descartador = getDescartador(iteracion, holdoutRatio);
                    Collection<Usuario> descartados = descartador.descartar(restantes);
                    if (holdoutRatio == 0 || !descartados.isEmpty()) {
                        int cantidadFuentesRestantes = contarFuentes(restantes);
                        double porcentajeFuentes = ((double) cantidadFuentesRestantes) / restantes.size();
                        System.out.println("Fuentes=" + (porcentajeFuentes * 100) + "%");
                        // if (porcentajeFuentes == 0 || porcentajeFuentes == 1) {
                        //    continue;
                        // }
                        System.out.println("Evaluando " + usuarioInicial.getScreenName() + "(" + usuarioInicial.getId() + ") :" + usuarioInicial.getFollowsCount() + "/" + usuarioInicial.getFollowersCount());

//                        List<Usuario> metodo1recomendadosTodos;
//                        Recolector recolectorMetodo1;
//                        long tiempoMetodo1;
//                        {
//                            Date startMethod1 = new Date();
//                            CompararUsuarios comparar = new CompararPorScore();
//                            TipoTransicion transicion = TipoTransicion.Binaria;
//                            recolectorMetodo1 = recolectorFuentes(recoleccionFactory, transicion, descartador, nIntermedio, nFinal, comparar);
//                            /*List<Usuario> fuentes = */ recolectorMetodo1.recolectar(usuarioInicial, permitirRedundancia);
//                            metodo1recomendadosTodos = recolectorMetodo1.getNiveles().get(recolectorMetodo1.getNiveles().size() - 1).getCrudo();
//                            tiempoMetodo1 = new Date().getTime() - startMethod1.getTime();
//                        }

//                        List<Usuario> metodo3recomendadosTodos;
//                        Recolector recolectorMetodo3;
//                        long tiempoMetodo3;
//                        {
//                            Date startMethod3 = new Date();
//                            CompararUsuarios comparar = new CompararPorScore();
//                            TipoTransicion transicion = TipoTransicion.Binaria;
//                            recolectorMetodo3 = recolectorAmigos(recoleccionFactory, transicion, descartador, nIntermedio, nFinal, comparar);
//                            /*List<Usuario> fuentes = */ recolectorMetodo3.recolectar(usuarioInicial, permitirRedundancia);
//                            metodo3recomendadosTodos = recolectorMetodo3.getNiveles().get(recolectorMetodo3.getNiveles().size() - 1).getCrudo();
//                            tiempoMetodo3 = new Date().getTime() - startMethod3.getTime();
//                        }
                        
                        List<Usuario> metodo2recomendadosTodos;
                        Recolector recolectorMetodo2;
                        long tiempoMetodo2;
                        {
                            Date startMethod2 = new Date();
                            CompararUsuarios comparar = new CompararPorScore();
                            TipoTransicion transicion = TipoTransicion.Probabilistica;
                            recolectorMetodo2 = recolectorFuentes(recoleccionFactory, transicion, descartador, nIntermedio, nFinal, comparar);
                            recolectorMetodo2.recolectar(usuarioInicial, permitirRedundancia);
                            metodo2recomendadosTodos = recolectorMetodo2.getNiveles().get(recolectorMetodo2.getNiveles().size() - 1).getCrudo();
                            tiempoMetodo2 = new Date().getTime() - startMethod2.getTime();
                            
                        }

                        List<Usuario> metodo4recomendadosTodos;
                        Recolector recolectorMetodo4;
                        long tiempoMetodo4;
                        {
                            Date startMethod4 = new Date();
                            CompararUsuarios comparar = new CompararPorScore();
                            TipoTransicion transicion = TipoTransicion.Probabilistica;
                            recolectorMetodo4 = recolectorAmigos(recoleccionFactory, transicion, descartador, nIntermedio, nFinal, comparar);
                            recolectorMetodo4.recolectar(usuarioInicial, permitirRedundancia);
                            metodo4recomendadosTodos = recolectorMetodo4.getNiveles().get(recolectorMetodo4.getNiveles().size() - 1).getCrudo();
                            tiempoMetodo4 = new Date().getTime() - startMethod4.getTime();
                        }
                        List<Usuario> metodoXrecomendadosTodos;
                        long timeRankingMethodX;
                        {
                            Date startUnion = new Date();
                            RankingUsuarios rankCM = new RankingUsuariosCompuestoLineal(100, 100);
                            Map<Usuario, Double> usuariosRankeadosX2 = rankCM.rank(metodo2recomendadosTodos);

                            RankingUsuarios rankS = getRanking(usuarioInicial, 2);
                            Map<Usuario, Double> usuariosRankeadosX6 = rankS.rank(metodo4recomendadosTodos);

                            CombinadorResultados unidor = new CombinadorResultadosUnionPrioritario();

                            Map<Usuario, Double> usuariosRankeadosUnion = unidor.combinar(usuariosRankeadosX2, usuariosRankeadosX6);
                            metodoXrecomendadosTodos = new ArrayList<Usuario>(usuariosRankeadosUnion.keySet());
                            Collections.sort(metodoXrecomendadosTodos, new CompararPorScoreExterno(usuariosRankeadosUnion));
                            timeRankingMethodX = tiempoMetodo2 + tiempoMetodo4 + new Date().getTime() - startUnion.getTime();
                            Collection<RecommendationReport> localReportsUnion = generarReportes(40, start, timeRankingMethodX, usuarioInicial, iteracion, nIntermedio, nMaxArcos, null, metodoXrecomendadosTodos, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "RX");
                            reports.addAll(localReportsUnion);
                        }
                        /*
                        List<Usuario> metodoXrecomendados = metodoXrecomendadosTodos;
                        if (RANKINGS > RANKINGSLAZY && MAXPOSICIONBUSQUEDA > 0) {
                            metodoXrecomendados = metodoXrecomendadosTodos.subList(0, Math.min(MAXPOSICIONBUSQUEDA, metodoXrecomendadosTodos.size()));
                            recoleccionFactory.verificar(metodoXrecomendados);
                        }
                        for (int ranking = 3; ranking <= RANKINGS; ++ranking) {
                            Date startRankingMethod = new Date();
                            RankingUsuarios rank = getRanking(usuarioInicial, ranking);
                            Map<Usuario, Double> usuariosRankeados = rank.rank(metodoXrecomendados);
                            Collections.sort(metodoXrecomendados, new CompararPorScoreExterno(usuariosRankeados));
                            long tiempoMetodoX = new Date().getTime() - startRankingMethod.getTime();
                            Collection<RecommendationReport> localReports = generarReportes(40, start, tiempoMetodoX + timeRankingMethodX, usuarioInicial, iteracion, nIntermedio, nMaxArcos, null, metodoXrecomendados, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "RX" + ranking);
                            reports.addAll(localReports);
                            //List<Usuario> fuentes = getFuentes(metodoXrecomendados);
                            //for(int i=0;i<fuentes.size();++i)
                             //   LOGGER.log(Level.INFO, ""+i+"/"+fuentes.size()+" : "+fuentes.get(i).getScreenName());
                        }*/
                        /*
                         for (int ranking = 0; ranking <= RANKINGS; ++ranking) {

                         if (ranking == 1) {
                         continue;
                         }
                         List<Usuario> metodo2recomendados;
                         List<Usuario> metodo4recomendados;
                         if (ranking > RANKINGSLAZY && MAXPOSICIONBUSQUEDA > 0) {
                         metodo2recomendados =  metodo2recomendadosShort;
                         metodo4recomendados =  metodo4recomendadosShort;
                         } else {
                         metodo2recomendados =  metodo2recomendadosTodos;
                         metodo4recomendados =  metodo4recomendadosTodos;
                         }

                         //                            Map<Usuario, Double> usuariosRankeados1 = aplicarRanking(usuarioInicial, metodo1recomendadosTodos, ranking);
                         //                            {
                         //                                Collections.sort(metodo1recomendadosTodos, new CompararPorScoreExterno(usuariosRankeados1));
                         //                                Collection<RecommendationReport> localReports1 = generarReportes(0, start, tiempoMetodo1, usuarioInicial, iteracion, nIntermedio, nMaxArcos, recolectorMetodo1, metodo1recomendadosTodos, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "R" + ranking);
                         //                                //reports.addAll(localReports1);
                         //                            }
                         Date startRankingMethod2 = new Date();
                         long timeRankingMethod2;
                         Map<Usuario, Double> usuariosRankeados2;
                         {
                         RankingUsuarios rank = getRanking(usuarioInicial, ranking);
                         usuariosRankeados2 = rank.rank(metodo2recomendados);
                         Collections.sort(metodo2recomendados, new CompararPorScoreExterno(usuariosRankeados2));
                         timeRankingMethod2 = new Date().getTime() - startRankingMethod2.getTime();
                         Collection<RecommendationReport> localReports2 = generarReportes(1, start, tiempoMetodo2 + timeRankingMethod2, usuarioInicial, iteracion, nIntermedio, nMaxArcos, recolectorMetodo2, metodo2recomendados, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "R" + ranking);
                         //reports.addAll(localReports2);
                         }
                         //                            Map<Usuario, Double> usuariosRankeados3 = aplicarRanking(usuarioInicial, metodo3recomendadosTodos, ranking);
                         //                            {
                         //                                Collections.sort(metodo3recomendadosTodos, new CompararPorScoreExterno(usuariosRankeados3));
                         //                                Collection<RecommendationReport> localReports3 = generarReportes(5, start, tiempoMetodo3, usuarioInicial, iteracion, nIntermedio, nMaxArcos, recolectorMetodo3, metodo3recomendadosTodos, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "R" + ranking);
                         //                                //reports.addAll(localReports3);
                         //                            }
                         Date startRankingMethod4 = new Date();
                         long timeRankingMethod4;
                         Map<Usuario, Double> usuariosRankeados4;
                         {
                         RankingUsuarios rank = getRanking(usuarioInicial, ranking);
                         usuariosRankeados4 = rank.rank(metodo4recomendados);
                         Collections.sort(metodo4recomendados, new CompararPorScoreExterno(usuariosRankeados4));
                         timeRankingMethod4 = new Date().getTime() - startRankingMethod4.getTime();
                         Collection<RecommendationReport> localReports4 = generarReportes(6, start, tiempoMetodo4 + timeRankingMethod4, usuarioInicial, iteracion, nIntermedio, nMaxArcos, recolectorMetodo4, metodo4recomendados, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "R" + ranking);
                         //reports.addAll(localReports4);
                         }
                         //fuentes + fuentes
                         //                            {
                         //                                CombinadorResultados unidor = new CombinadorResultadosUnion(new CombinadorPuntajeProducto(), 0.1);
                         //                                Date startUnion = new Date();
                         //                                Map<Usuario, Double> usuariosRankeadosUnion = unidor.combinar(usuariosRankeados1, usuariosRankeados2);
                         //                                List<Usuario> usuariosUnion = new ArrayList<Usuario>(usuariosRankeadosUnion.keySet());
                         //                                long tiempoUnion = new Date().getTime() - startUnion.getTime();
                         //                                Collections.sort(usuariosUnion, new CompararPorScoreExterno(usuariosRankeadosUnion));
                         //                                Collection<RecommendationReport> localReportsUnion = generarReportes(3, start, tiempoMetodo1 + tiempoMetodo2 + tiempoUnion, usuarioInicial, iteracion, nIntermedio, nMaxArcos, null, usuariosUnion, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "R" + ranking);
                         //                                //reports.addAll(localReportsUnion);
                         //                            }
                         //                            {
                         //                                CombinadorResultados intersector = new CombinadorResultadosInterseccion(new CombinadorPuntajeProducto());
                         //                                Date startUnion = new Date();
                         //                                Map<Usuario, Double> usuariosRankeadosIntersseccion = intersector.combinar(usuariosRankeados1, usuariosRankeados2);
                         //                                List<Usuario> usuariosInterseccion = new ArrayList<Usuario>(usuariosRankeadosIntersseccion.keySet());
                         //                                long tiempoInterseccion = new Date().getTime() - startUnion.getTime();
                         //                                Collections.sort(usuariosInterseccion, new CompararPorScoreExterno(usuariosRankeadosIntersseccion));
                         //                                Collection<RecommendationReport> localReportsInterseccion = generarReportes(4, start, tiempoMetodo1 + tiempoMetodo2 + tiempoInterseccion, usuarioInicial, iteracion, nIntermedio, nMaxArcos, null, usuariosInterseccion, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "R" + ranking);
                         //                                //reports.addAll(localReportsInterseccion);
                         //                            }
                         //amigos+amigos
                         //                            {
                         //                                CombinadorResultados unidor = new CombinadorResultadosUnion(new CombinadorPuntajeProducto(), 0.1);
                         //                                Date startUnion = new Date();
                         //                                Map<Usuario, Double> usuariosRankeadosUnion = unidor.combinar(usuariosRankeados3, usuariosRankeados4);
                         //                                List<Usuario> usuariosUnion = new ArrayList<Usuario>(usuariosRankeadosUnion.keySet());
                         //                                long tiempoUnion = new Date().getTime() - startUnion.getTime();
                         //                                Collections.sort(usuariosUnion, new CompararPorScoreExterno(usuariosRankeadosUnion));
                         //                                Collection<RecommendationReport> localReportsUnion = generarReportes(7, start, tiempoMetodo3 + tiempoMetodo4 + tiempoUnion, usuarioInicial, iteracion, nIntermedio, nMaxArcos, null, usuariosUnion, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "R" + ranking);
                         //                                //reports.addAll(localReportsUnion);
                         //                            }
                         //                            {
                         //                                CombinadorResultados intersector = new CombinadorResultadosInterseccion(new CombinadorPuntajeProducto());
                         //                                Date startUnion = new Date();
                         //                                Map<Usuario, Double> usuariosRankeadosIntersseccion = intersector.combinar(usuariosRankeados3, usuariosRankeados4);
                         //                                List<Usuario> usuariosInterseccion = new ArrayList<Usuario>(usuariosRankeadosIntersseccion.keySet());
                         //                                long tiempoInterseccion = new Date().getTime() - startUnion.getTime();
                         //                                Collections.sort(usuariosInterseccion, new CompararPorScoreExterno(usuariosRankeadosIntersseccion));
                         //                                Collection<RecommendationReport> localReportsInterseccion = generarReportes(8, start, tiempoMetodo3 + tiempoMetodo4 + tiempoInterseccion, usuarioInicial, iteracion, nIntermedio, nMaxArcos, null, usuariosInterseccion, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "R" + ranking);
                         //                                //reports.addAll(localReportsInterseccion);
                         //                            }
                         //fuentes+amigos
                         //                            {
                         //                                CombinadorResultados unidor = new CombinadorResultadosUnion(new CombinadorPuntajeProducto(), 0.1);
                         //                                Date startUnion = new Date();
                         //                                Map<Usuario, Double> usuariosRankeadosUnion = unidor.combinar(usuariosRankeados2, usuariosRankeados4);
                         //                                List<Usuario> usuariosUnion = new ArrayList<Usuario>(usuariosRankeadosUnion.keySet());
                         //                                Collections.sort(usuariosUnion, new CompararPorScoreExterno(usuariosRankeadosUnion));
                         //                                long tiempoUnion = new Date().getTime() - startUnion.getTime();
                         //                                Collection<RecommendationReport> localReportsUnion = generarReportes(9, start, tiempoMetodo2 + tiempoMetodo4 + tiempoUnion + timeRankingMethod2 + timeRankingMethod4, usuarioInicial, iteracion, nIntermedio, nMaxArcos, null, usuariosUnion, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "R" + ranking);
                         //                                //reports.addAll(localReportsUnion);
                         //                            }
                         //                            {
                         //                                CombinadorResultados intersector = new CombinadorResultadosInterseccion(new CombinadorPuntajeProducto());
                         //                                Date startUnion = new Date();
                         //                                Map<Usuario, Double> usuariosRankeadosIntersseccion = intersector.combinar(usuariosRankeados2, usuariosRankeados4);
                         //                                List<Usuario> usuariosInterseccion = new ArrayList<Usuario>(usuariosRankeadosIntersseccion.keySet());
                         //                                Collections.sort(usuariosInterseccion, new CompararPorScoreExterno(usuariosRankeadosIntersseccion));
                         //                                long tiempoInterseccion = new Date().getTime() - startUnion.getTime();
                         //                                Collection<RecommendationReport> localReportsInterseccion = generarReportes(10, start, tiempoMetodo2 + tiempoMetodo4 + tiempoInterseccion + timeRankingMethod2 + timeRankingMethod4, usuarioInicial, iteracion, nIntermedio, nMaxArcos, null, usuariosInterseccion, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, "R" + ranking);
                         //                                //reports.addAll(localReportsInterseccion);
                         //                            }
                         }
                         */
                        //reportDao.update(reports);
                    }
                }
            }
        }
        return reports;
    }
    int RANKINGS = 3;
    int RANKINGSLAZY = 3;
    int nRecomendados = MAXPOSICIONBUSQUEDA;
    int MAX_USUARIOS = MAXPOSICIONBUSQUEDA;

    private RankingUsuarios getRanking(Usuario usuarioInicial, int r) {

        RankingUsuarios ranking = null;
        if (r == 0) {
            ranking = new RankingUsuariosPeso();
        } else if (r == 1) {
            ranking = new RankingUsuariosMenciones();
        } else if (r == 2) {
            ranking = new RankingUsuariosCompuesto(new RankingUsuariosMenciones(), new RankingUsuariosPeso(), new CombinadorPuntajeProducto());
        } else if (r == 3) {
            ranking = new RankingUsuariosIS();
        } else if (r == 4) {
            ranking = new RankingUsuariosHannonS6(usuarioInicial, 41000000, recoleccionFactory);
        }
        return ranking;
    }

    private Collection<RecommendationReport> generarReportes(int metodo, Date start, long tiempo, Usuario usuarioInicial, int iteracion, int nIntermedio, int nMaxArcos, Recolector recolector, List<Usuario> usuariosRankeados, int nRecomendados, Collection<Usuario> descartados, List<Usuario> restantes, boolean permitirRedundancia, int cantidadFuentes, int cantidadFuentesRestantes, String ranking) {
        Collection<RecommendationReport> reports = new ArrayList<RecommendationReport>();
        RecommendationReport reportTodos = generateReport(metodo, start, tiempo, usuarioInicial, iteracion, nIntermedio, nMaxArcos, recolector, usuariosRankeados, nRecomendados, descartados, restantes, permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, ranking);
        reportTodos.setMetodo(RecommendationReport.METODO_UNION);
        reports.add(reportTodos);

        RecommendationReport reportFuentes = generateReport(metodo + 10, start, tiempo, usuarioInicial, iteracion, nIntermedio, nMaxArcos, recolector, getFuentes(usuariosRankeados, MAX_USUARIOS), nRecomendados, getFuentes(descartados), getFuentes(restantes), permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, ranking);
        reportFuentes.setMetodo(RecommendationReport.METODO_FUENTES);
        reports.add(reportFuentes);

        RecommendationReport reportAmigos = generateReport(metodo + 20, start, tiempo, usuarioInicial, iteracion, nIntermedio, nMaxArcos, recolector, getAmigos(usuariosRankeados, MAX_USUARIOS), nRecomendados, getAmigos(descartados), getAmigos(restantes), permitirRedundancia, cantidadFuentes, cantidadFuentesRestantes, ranking);
        reportAmigos.setMetodo(RecommendationReport.METODO_AMIGOS);
        reports.add(reportAmigos);

        return reports;
    }

    private int[] getPosiciones(Collection<Usuario> subconjunto, List<Usuario> conjunto) {
        int[] posiciones = new int[subconjunto.size()];
        for (int p = 0; p < subconjunto.size(); ++p) {
            posiciones[p] = RecommendationReport.POSICION_NO_ENCONTRADO;
        }


        int encontrados = 0;
        int u = 0;
        for (Usuario usuario : subconjunto) {
            int p = conjunto.indexOf(usuario);
            if (p != -1) {
                // System.out.println("Encontrado " + usuario.getScreenName() + " en " + p + " " + encontrados + "/" + subconjunto.size());
                posiciones[u++] = p;
                encontrados++;
            } else {
                posiciones[u] = RecommendationReport.POSICION_NO_ENCONTRADO;
            }
        }
        LOGGER.log(Level.INFO, "Encontrados: " + encontrados + "/" + subconjunto.size());
        System.out.println();
        return posiciones;
    }

    private float[] getScore(List<Usuario> usuarios) {
        float[] array = new float[usuarios.size()];
        int d = 0;
        for (Usuario u : usuarios) {
            array[d++] = (float) u.getScore();
        }
        return array;
    }

    private RecommendationReport generateReport(int metodo, Date start, long elapsedTime, Usuario usuarioInicial, int iteracion, int nIntermedio, int nMaxArcos, Recolector recolector, List<Usuario> usuariosTodos, int nRecomendados, Collection<Usuario> descartados, List<Usuario> restantes, boolean permitirRedundancia, int cantidadFuentes, int cantidadFuentesRestantes, String ranking) {
        RecommendationReport report = new RecommendationReport();
        report.setCantidadFuentes(cantidadFuentes);
        report.setCantidadFuentesRestantes(cantidadFuentesRestantes);
        report.setDate(start);
        report.setElapsedTime(elapsedTime);
        report.setMetodo(metodo);
        report.setPermitirRedundancia(permitirRedundancia);
        report.setUsuario(usuarioInicial);
        report.setIteracion(iteracion);
        if (recolector != null) {
            report.setRecolector(recolector);
            report.setRecuentoNodos(recolector.evaluados);
        }
        report.setRecuentoHojas(usuariosTodos.size());
        //usuariosTodos= new ArrayList<Usuario>(usuariosTodos);
        if (nRecomendados > 0 && !usuariosTodos.isEmpty()) {
            List<Usuario> recomendacion = new ArrayList<Usuario>();
            int aRecomendar = Math.min(nRecomendados, usuariosTodos.size());
            for (int i = 0; i < aRecomendar; ++i) {
                recomendacion.add(usuariosTodos.get(i));
                //LOGGER.log(Level.INFO, "Recomendado: " + usuariosTodos.get(i) + "/" + aRecomendar);
            }
            report.setRecomendacion(recomendacion);
            report.setScore(getScore(recomendacion));
        }
        if (descartados != null && !descartados.isEmpty()) {
            report.setHoldoutSize(descartados.size());
            report.setHoldinSize(restantes.size());


            //Collections.sort(usuariosTodos, new CompararPorOcurrencias());



            List<Usuario> encontrados = new ArrayList<Usuario>();
            List<Usuario> noEncontrados = new ArrayList<Usuario>();
            int[] posiciones = getPosiciones(descartados, usuariosTodos);
            int d = 0;
            for (Usuario descartado : descartados) {
                descartado.setPosicion(RecommendationReport.POSICION_NO_ENCONTRADO);
                if (posiciones[d] != RecommendationReport.POSICION_NO_ENCONTRADO) {
                    Usuario encontrado = usuariosTodos.get(posiciones[d]);
                    encontrado.setPosicion(posiciones[d] + 1);
                    encontrados.add(encontrado);
                } else {
                    noEncontrados.add(descartado);
                }
                ++d;
            }
            System.out.println("5");
            //calcula la cantidad de fuentes recuperadas
            report.setCantidadFuentesRecuperadas(contarFuentes(encontrados));
            //agrega las no recuperadas al resultado (posicion-1)

            Collections.sort(encontrados, new CompararPorPosicion());
            for (int e = 0; e < encontrados.size(); ++e) {
                posiciones[e] = encontrados.get(e).getPosicion();
            }
            String tag = "" + nIntermedio + "-" + nMaxArcos + "-" + report.isPermitirRedundancia() + "-" + ranking;
            String folder = "D:\\tesis\\twitter\\results-" + tag;
            new File(folder).mkdirs();
            guardarReporte(report, folder + "\\reporte" + metodo + "-" + tag + ".csv", encontrados.size());
            listarEncontrados(metodo, usuarioInicial.getId(), encontrados, descartados.size(), report.getHoldinSize(), folder + "\\encontrados" + metodo + "-" + tag + ".csv", report.getRecuentoHojas(), report.getElapsedTime());
            // listarEncontrados(metodo, usuarioInicial.getId(), getFuentes(encontrados), getFuentes(descartados).size(), report.getHoldinSize(), folder + "\\encontrados_fuentes" + metodo + "-" + tag + ".csv", report.getRecuentoHojas(), report.getElapsedTime());
            // listarEncontrados(metodo, usuarioInicial.getId(), getAmigos(encontrados), getAmigos(descartados).size(), report.getHoldinSize(), folder + "\\encontrados_amigos" + metodo + "-" + tag + ".csv", report.getRecuentoHojas(), report.getElapsedTime());

            encontrados.addAll(noEncontrados);

            report.setHoldout(encontrados);
            report.setPosicion(posiciones);
            System.out.println("6");

        }

        return report;
    }
    String separator = ";";

    private void listarEncontrados(int metodo, long id, Collection<Usuario> encontrados, int holdout, int holdin, String path, int hojas, long tiempo) {
        BufferedWriter writer = null;
        try {
            File file = new File(path);
            writer = new BufferedWriter(new FileWriter(file, true));
            String line = "" + metodo + separator + id + separator + encontrados.size() + separator + holdout + separator + holdin + separator + hojas + separator + tiempo;
            for (Usuario encontrado : encontrados) {
                line += separator + encontrado.getPosicion();
            }
            writer.write(line + "\n");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // Close the writer regardless of what happens...
                writer.close();
            } catch (Exception e) {
            }
        }
    }

    private void guardarReporte(RecommendationReport report, String path, int encontrados) {
        BufferedWriter writer = null;
        try {
            File file = new File(path);
            writer = new BufferedWriter(new FileWriter(file, true));
            String line = "";
            line += report.getMetodo() + separator;
            line += report.getUsuarioId() + separator;
            line += report.getIteracion() + separator;
            line += report.getHoldoutSize() + separator;
            line += report.getHoldinSize() + separator;
            line += report.getCantidadFuentes() + separator;
            line += report.getCantidadFuentesRestantes() + separator;
            line += report.getRecuentoHojas() + separator;
            line += report.getElapsedTime() + separator;
            line += report.getCantidadFuentesRecuperadas() + separator;
            line += encontrados;
            writer.write(line + "\n");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // Close the writer regardless of what happens...
                writer.close();
            } catch (Exception e) {
            }
        }
    }
}
